package one.stand.eureka.info;

import com.netflix.appinfo.AmazonInfo;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.DataCenterInfo;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.config.ConfigurationManager;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Pair;
import com.netflix.eureka.EurekaServerContext;
import com.netflix.eureka.EurekaServerContextHolder;
import com.netflix.eureka.cluster.PeerEurekaNode;
import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
import com.netflix.eureka.resources.StatusResource;
import com.netflix.eureka.util.StatusInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;

import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.util.*;

public class ApplicationInfoManagerHandler {

    @Value("${eureka.dashboard.path:/}")
    private String dashboardPath = "";

    private ApplicationInfoManager applicationInfoManager;

    public ApplicationInfoManagerHandler(ApplicationInfoManager applicationInfoManager) {
        this.applicationInfoManager = applicationInfoManager;
    }

    public void populateBase(HttpServletRequest request, Map<String, Object> model) {
        model.put("time", new Date());
        model.put("basePath", "/");
        model.put("dashboardPath", this.dashboardPath.equals("/") ? ""
                : this.dashboardPath);
        populateHeader(model);
        populateNavbar(request, model);
    }

    public boolean isUp(String name) {
        List<Application> sortedApplications = getRegistry().getSortedApplications();
        Application application = getApp(sortedApplications, name);
        if (Objects.isNull(application)) return false;
        if (application.getInstances().size() <= 0) return false;

        for (InstanceInfo info : application.getInstances()) {
            if (info.getStatus() != InstanceInfo.InstanceStatus.UP) {
                return false;
            }
        }
        return true;
    }

    public List<Map<String, Object>> getInstanceInfos(String name) {
        List<Application> sortedApplications = getRegistry().getSortedApplications();
        Application application = getApp(sortedApplications, name);
        List<Map<String, Object>> instanceInfos = new ArrayList<>();
        if (Objects.nonNull(application)) {
            Map<String, Object> map;
            for (InstanceInfo info : application.getInstances()) {
                map = new HashMap<>();
                map.put("id", info.getId());
                map.put("status", info.getStatus());
                map.put("isNotUp", info.getStatus() != InstanceInfo.InstanceStatus.UP);
                instanceInfos.add(map);
            }
        }
        return instanceInfos;
    }

    public LinkedHashMap<String, Object> get(List<Application> sortedApplications, String name, String lastVersion) {
        Application app = getApp(sortedApplications, name);
        if (Objects.isNull(app)) {
            app = new Application();
            app.setName(name);
        }

        LinkedHashMap<String, Object> appData = new LinkedHashMap<>();
        appData.put("name", app.getName());

        Map<String, Integer> amiCounts = new HashMap<>();
        Map<InstanceInfo.InstanceStatus, List<Pair<String, String>>> instancesByStatus = new HashMap<>();
        Map<String, Integer> zoneCounts = new HashMap<>();
        for (InstanceInfo info : app.getInstances()) {
            String id = info.getId();
            String url = info.getStatusPageUrl();
            InstanceInfo.InstanceStatus status = info.getStatus();
            String ami = "n/a";
            String zone = "";
            if (info.getDataCenterInfo().getName() == DataCenterInfo.Name.Amazon) {
                AmazonInfo dcInfo = (AmazonInfo) info.getDataCenterInfo();
                ami = dcInfo.get(AmazonInfo.MetaDataKey.amiId);
                zone = dcInfo.get(AmazonInfo.MetaDataKey.availabilityZone);
            }
            Integer count = amiCounts.get(ami);
            if (count != null) {
                amiCounts.put(ami, count + 1);
            } else {
                amiCounts.put(ami, 1);
            }
            count = zoneCounts.get(zone);
            if (count != null) {
                zoneCounts.put(zone, count + 1);
            } else {
                zoneCounts.put(zone, 1);
            }
            List<Pair<String, String>> list = instancesByStatus.get(status);
            if (list == null) {
                list = new ArrayList<>();
                instancesByStatus.put(status, list);
            }
            list.add(new Pair<>(id, url));
        }
        appData.put("amiCounts", amiCounts.entrySet());
        appData.put("zoneCounts", zoneCounts.entrySet());
        ArrayList<Map<String, Object>> instanceInfos = new ArrayList<>();
        appData.put("instanceInfos", instanceInfos);

        boolean versionLast = true;

        for (Iterator<Map.Entry<InstanceInfo.InstanceStatus, List<Pair<String, String>>>> iter = instancesByStatus
                .entrySet().iterator(); iter.hasNext(); ) {
            Map.Entry<InstanceInfo.InstanceStatus, List<Pair<String, String>>> entry = iter
                    .next();
            List<Pair<String, String>> value = entry.getValue();
            InstanceInfo.InstanceStatus status = entry.getKey();
            LinkedHashMap<String, Object> instanceData = new LinkedHashMap<>();
            instanceInfos.add(instanceData);
            instanceData.put("status", entry.getKey());
            ArrayList<Map<String, Object>> instances = new ArrayList<>();
            instanceData.put("instances", instances);
            instanceData.put("isNotUp", status != InstanceInfo.InstanceStatus.UP);

            // TODO

            /*
             * if(status != InstanceInfo.InstanceStatus.UP){
             * buf.append("<font color=red size=+1><b>"); }
             * buf.append("<b>").append(status
             * .name()).append("</b> (").append(value.size()).append(") - ");
             * if(status != InstanceInfo.InstanceStatus.UP){
             * buf.append("</font></b>"); }
             */

            for (Pair<String, String> p : value) {
                LinkedHashMap<String, Object> instance = new LinkedHashMap<>();
                instances.add(instance);
                instance.put("id", p.first());
                String url = p.second();
                instance.put("url", url);
                boolean isHref = url != null && url.startsWith("http");
                instance.put("isHref", isHref);
                /*
                 * String id = p.first(); String url = p.second(); if(url != null &&
                 * url.startsWith("http")){
                 * buf.append("<a href=\"").append(url).append("\">"); }else { url =
                 * null; } buf.append(id); if(url != null){ buf.append("</a>"); }
                 * buf.append(", ");
                 */
            }

            for (Map<String, Object> instance : instances) {
                String id = String.valueOf(instance.get("id"));
                String[] ids = id.split(":");
                String currentVersion = ids[ids.length - 1];
                if (!StringUtils.equals(lastVersion, currentVersion)) {
                    versionLast = false;
                    break;
                }
            }
        }

        appData.put("versionLast", versionLast);

        return appData;
    }

    private Application getApp(List<Application> applications, String name) {
        for (Application application : applications) {
            if (name.equals(application.getName())) {
                return application;
            }
        }
        return null;
    }


    private void populateHeader(Map<String, Object> model) {
        model.put("currentTime", StatusResource.getCurrentTimeAsString());
        model.put("upTime", StatusInfo.getUpTime());
        model.put("environment", ConfigurationManager.getDeploymentContext()
                .getDeploymentEnvironment());
        model.put("datacenter", ConfigurationManager.getDeploymentContext()
                .getDeploymentDatacenter());
        PeerAwareInstanceRegistry registry = getRegistry();
        model.put("registry", registry);
        model.put("isBelowRenewThresold", registry.isBelowRenewThresold() == 1);
        DataCenterInfo info = applicationInfoManager.getInfo().getDataCenterInfo();
        if (info.getName() == DataCenterInfo.Name.Amazon) {
            AmazonInfo amazonInfo = (AmazonInfo) info;
            model.put("amazonInfo", amazonInfo);
            model.put("amiId", amazonInfo.get(AmazonInfo.MetaDataKey.amiId));
            model.put("availabilityZone",
                    amazonInfo.get(AmazonInfo.MetaDataKey.availabilityZone));
            model.put("instanceId", amazonInfo.get(AmazonInfo.MetaDataKey.instanceId));
        }
    }

    private void populateNavbar(HttpServletRequest request, Map<String, Object> model) {
        Map<String, String> replicas = new LinkedHashMap<>();
        List<PeerEurekaNode> list = getServerContext().getPeerEurekaNodes().getPeerNodesView();
        for (PeerEurekaNode node : list) {
            try {
                URI uri = new URI(node.getServiceUrl());
                String href = scrubBasicAuth(node.getServiceUrl());
                replicas.put(uri.getHost(), href);
            } catch (Exception ex) {
                // ignore?
            }
        }
        model.put("replicas", replicas.entrySet());
    }

    private String scrubBasicAuth(String urlList) {
        String[] urls = urlList.split(",");
        StringBuilder filteredUrls = new StringBuilder();
        for (String u : urls) {
            if (u.contains("@")) {
                filteredUrls.append(u.substring(0, u.indexOf("//") + 2)).append(u.substring(u.indexOf("@") + 1, u.length())).append(",");
            } else {
                filteredUrls.append(u).append(",");
            }
        }
        return filteredUrls.substring(0, filteredUrls.length() - 1);
    }

    private PeerAwareInstanceRegistry getRegistry() {
        return getServerContext().getRegistry();
    }

    private EurekaServerContext getServerContext() {
        return EurekaServerContextHolder.getInstance().getServerContext();
    }
}
